package org.web.base.domain;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.Set;


/**
 * 类QueryBase.java的实现描述：所有查询DO的父类.封装通用查询属性信息.
 *
 * @author Luyl 2015年9月29日 下午5:24:41
 */
@Setter
@Getter
public class QueryBase implements Serializable {

    /**
     * 第一页，分页查询的第一页。
     */
    public static final Integer FIRST_PAGE = 1;

    /**
     * 每分页记录数是1，每个分页是1条记录。
     */
    public static final Integer ONE_SIZE_PAGE = 1;

    /**
     * 默认分页数量大小，默认每页是10条记录。
     */
    public static final Integer DEFAULT_PAGE_SIZE = 10;


    public static final Integer MAX_SIZE_PAGE = 100;


    /**
     * 分页查询，指定查询第几页，不指定，默认为第一页。
     */
    private Integer pageNum = FIRST_PAGE;

    /**
     * 分页查询，每页记录数
     */

    private Integer pageSize = DEFAULT_PAGE_SIZE;

    /**
     * 开始记录数，可以不进行指定，可以通过pageSize与pageNum推算得到，详情见getStartNum()方法。
     * 执行的SQL查询语句，都统一使用getStartNum()。
     * 如果设置startNum（startNum不能小于0，最小值可以等于0）， 则startNum的优先级高于pageSize和pageNum。
     */
    private Integer startNum = null;

    /**
     * 查询的开始记录数。最小值为0。
     * 该方法，是数据库执行的真正参数，执行的SQL查询语句，都统一使用getStartNum()。
     * 在该方法中，会校验参数的合法性，如果数据不合法，会转义成默认规则。
     * （1）可以直接指定 startNum，如果startNum为空或者小于 0 ，则会根据 pageNum与 pageSize计算分页信息。
     * （2）会校验 pageNum 的合法性，如果 pageNum 为空或者小于 0，会设置成查询第一页。
     * （3）会校验 pageSize 的合法性，如果 pageSize 为空或者小于 0， 会设置分页数量为 DEFAULT_PAGE_SIZE。
     * （4） 在未指定 startNum 的场合，根据 pageNum与 pageSize 查询记录的初始位置。
     */
    public Integer getStartNum() {
        if (startNum == null || startNum < 0) {
            if (pageNum == null || pageNum < FIRST_PAGE) {
                pageNum = FIRST_PAGE;
            }
            if (pageSize == null || pageSize < ONE_SIZE_PAGE) {
                pageSize = DEFAULT_PAGE_SIZE;
            }
            return (pageNum - 1) * pageSize;
        }
        return startNum;
    }

    /**
     * 只获取第一条记录。
     */
    public void setFirstRecord() {
        this.pageNum = FIRST_PAGE;
        this.pageSize = ONE_SIZE_PAGE;
    }

    /**
     * 获取所有记录。
     */
    public void setAllRecords() {
        this.pageNum = FIRST_PAGE;
        this.pageSize = Integer.MAX_VALUE;
    }


    /**
     * 查询去重列信息。
     */
    private String distinct;

    /**
     * 排序查询语句。
     */
    private String orderByClause = null;

    /**
     * 是否需要查询总记录数，默认不进行查询记录总数。
     */
    private boolean needCountTotal = false;

    /**
     * 查询指定属性信息。用于列字段过多，减少非必要字段传输，只获取关心的属性信息。
     * 入参格式：下划线分隔。多个字段用逗号【,】分隔。
     * 如果属性信息不存在，则抛出异常。
     */
    private Set<String> specifiedAttributeSet;

    /**
     * 查询总记录数的属性名。如果不指定，默认使用 count(1)， 使用时需要保证在指定的实体中有该属性信息。
     * 入参格式：驼峰和下划线都可以，如果是驼峰，则会自动转成下划线。
     * 需要保证在指定的实体中有该属性信息。
     */
    private String countAttributeName;

    /**
     * 查询指定属性信息。
     */
    public String getSpecifiedColumns() {
        if (specifiedAttributeSet != null && !specifiedAttributeSet.isEmpty()) {
            return String.join(",", specifiedAttributeSet);
        }
        return null;
    }


    /**
     * 需执行非空检查的属性集合（IS NOT NULL）
     * <p>
     * 存储需要转换为 SQL 条件 "column IS NOT NULL" 的属性名称，
     * 集合元素为 Java 实体类的属性名，后续会通过转换逻辑映射为数据库字段名。
     * 例如：若集合包含 "userName"，则可能生成条件 "user_name IS NOT NULL"
     */
    private Set<String> nonNullCheckedAttributes;

    /**
     * 需执行空值检查的属性集合（IS NULL）
     * <p>
     * 存储需要转换为 SQL 条件 "column IS NULL" 的属性名称，
     * 集合元素为 Java 实体类的属性名，与数据库字段的映射规则同非空检查集合。
     * 例如：若集合包含 "userEmail"，则可能生成条件 "user_email IS NULL"
     */
    private Set<String> nullCheckedAttributes;

}
